#ifndef MULTIGENEREGIONDATAUPLOADER_H
#define MULTIGENEREGIONDATAUPLOADER_H

#include <QObject>
#include <QString>
#include <QDebug>
#include <QProcess>
#include <QFile>
#include <QDir>
#include <QApplication>
#include <QThread>

#include "fastaclustergenerator.h"
#include "../../sagevis.h";

#include "BaseDataManagement/genomedatabaseinterface.h"
#include "PubMedDataManagement/pubmedgenomelocaldatabaseinterface.h"
#include "ProprietaryDataManagement/proprietarygenomelocaldatabaseinterface.h"

#include "BaseDataManagement/gffreader.h"
#include "ProprietaryDataManagement/proprietarygffreader.h"
#include "PubMedDataManagement/pubmedgffreader.h"

#include "BaseDataManagement/genomeelementdatapacket.h"
#include "ProprietaryDataManagement/proprietarygenomeelementdatapacket.h"
#include "PubMedDataManagement/pubmedgenomeelementdatapacket.h"

#include "BaseDataManagement/contigdatapacket.h"

/**GenomeDataUploader class.
  * SUMMARY:
  * This class
  * 1) opens gff and fasta files
  * 2) launches threads to process these files
  * 3) outputs data packets that contain data for each genome element and contig
  *
  * INPUT:
  * 1) List of Gff Files
  * 2) List of Fasta Files
  *
  * Current file types that can be handled:  Proprietary and Pubmed
  * Currently handles upload of multiple gff and fasta files, but this has not been tested yet.
  * I would also like to eventually account for directories filled with gff and fasta files, but have not done this yet.
  *
  * OUTPUT
  * At the end of the processing are 'new' data packets, which this class puts into the QList and gives to MultiGeneRegionVisWidget- which manages the destruction of these pointers
  *  1) Pointer to a databaseInterface- created in multigenomeviswidget
  *  2) List of pointers to new genome element data packets
  *  3) List of pointers to new contig data packets
  *
  * PROCESSING DETAILS
  * Gff files are processed using one set of threads- one thread per gff file.
  * Fasta files are processed using another set of threads - one thread per fasta file.
  *
  * GFF FILE PROCESSING:
  * To handle different file types, I have created folders for different data sources that include derived
  * - gffReaders
  * - genomeElementDataPackets
  * - genomeDatabaseInterfaces
  * and may or may not include derived
  * - contigDataPacket
  *
  * Using the constant 'DATA_TYPE', I launch the appropriate gffReader, which hands back data packets.
  *     Note:  later, I won't want to have'DATA_TYPE' be constant.  Suppose someone wants to launch files from differnet sources'
  *             I will have to account for this eventuall.
  *
  * FASTA FILE PROCESSING:
  * Multiple fasta files may be provided:  faa (amino acid files for CDS), frn (for non-coding RNA) or fna (for nucleic acids).
  * faa files and frn files will be run through cd-hit algorithm which groups the elements into clusters that can be used to
  * 'link' elements together.
  * I don't yet have a solution for fna files.  I AM ASSUMING FAA OR FRN, but I have not tested frn.
  * To handle fna, I will likely use gff files to produce some sort of 'fge' - or
  * genome elements- which can then be run through cd_hit.
  *
  * Once these fasta files are run through cd_hit, there will be a set cluster files.
  * These files need to be processed in order to
  *   1) resolve duplicate cluster ids- since an RNA elt could be put in cluster1 in one file and a CDS could be in cluster1 in another
  *   2) create associativity between elt id and cluster id
  *         Elt id, which will be key into gff files, varies by file type and origin, so need multiple processing pipelines for this as well
*/
class GenomeDataUploader : public QObject
{
    Q_OBJECT
public:    
    /** GenomeDataUploader constructor with multiple gff and fasta files.
      * Constructor requires QList of gff files and QList of fasta files, plus a parent QObject
      * Constructor saves the names of these files.  Will process them in the 'process' function.
      * @param QList< QString > gffFiles, holds a list of gff files to be processed
      * @param QList< QString > fastaFiles, holds a list of fasta files (faa, frn, fna) to be processed
      * @param QObject parent, parent object, is set to null.
      */
    explicit GenomeDataUploader( QList< QString > gffFiles, QList< QString > fastaFiles, QObject *parent = 0);
    explicit GenomeDataUploader( QString gffFile, QString fastaFile, QObject *parent = 0);


signals:
    /** trackProgress(int stepsComplete) signal.
      * This signal, if caught by a parent class, communicates how much of the upload and processing is complete
      * @param int stepsComplete, stores a value out of 100
      */
    void trackProgress(int stepsComplete);

    /** finished()
      * This signal communicates the end of the data processing
      */
    void finished();

    /** error(QString err)
      * This signal passes along errors to a parent class.
      */
    void error( QString err );

    void dataUploadCompleteSignal( QList< QPointer < GenomeElementDataPacket> > genomeElementDataPackets, QList< QPointer < ContigDataPacket > > contigDataPackets , QPointer< GenomeDatabaseInterface > databaseInterface);

public slots:
    /** process() slot.
      * First step in the data processing:
      * It launches the FastaClusterGenerator threads to process faa and frn files.
      * It will eventually launch the partitioning of fna into frn and faa files, that can then be passed to FastaClusterGenerators
      * It will launch in parallel the processing of gff files.
      * It should be called when the thread is initialized.
      */
    void process();

    /** clusterFileGeneratedSlot(QString clusterFileName).
      * When a cluster file is generated by cdhit algorithm running in the fasta cluster generator class, this slot
      * recieves and stores the file name, for later access
      * @param QString clusterFileName
      */
    void clusterFileGeneratedSlot(QString clusterFileName);

    /** dataPacketGeneratedSlot(GenomeElementDataPacket* dataPacket).
      * As the gffReader runs, it generates genomeElementDataPackets, which are caught by this function.
      * As these data packets come in, contigsDataPackets are constructed.  Pointers to all the genome elements that are added to
      * contigDataPackets are saved in a separate list. These elements are also passed to the database, which saves and stores these elements.
      * Once 1000 contigDataPackets are constructed, new object construction halts and remaining data is only stored in database.
      * @param GenomeElementDataPacket* dataPacket.
      */
    void dataPacketGeneratedSlot(GenomeElementDataPacket* dataPacket);

    /** completedGffFileReadSlot();.
      * Once the gffReader finishes processing the gff file, this slot is called.
      * This slot keeps track of the number of processed gff files.  Once all of the files have been processed,
      * @param GenomeElementDataPacket* dataPacket.
      */
    void completedGffFileReadSlot();

private:
    QList< QString > gffFiles; /**< list of gff files to process. */
    QList< QString > fastaFiles; /**< list of fasta files to process. faa, fna or frn.*/

    QPointer<GenomeDatabaseInterface> databaseInterface;/**Pointer to the database that will manage the data.*/

    QList< QPointer < GenomeElementDataPacket> > genomeElementDataPackets; /**Pointer to the list of data for genome elements to display.*/

    QList< QPointer < ContigDataPacket > > contigDataPackets;/**List of contig data packets*/
    QHash< QString, QPointer < ContigDataPacket > > contigDataPacketHash;/**Hashtable of contigDataPackets.  This makes it easy to find contigs matching the contig of new genome elements.*/

    int countGffFilesRead;/**countGffFilesRead stores number of GffFiles read by gffReaders, so that this class knows to proceed to next data processing step.*/

    QList< QString > clusterFiles;/**< list of cluster files output from cdhit algorithm- one per fasta file.*/
    QHash< QString, QString > geneIdToClusterHashTable;

    void processFastaFiles();

    /** generateClusterHashTable().
      * Take all cluster files and create a single hashtable to link clusterId to eltID
      * Need to ensure that each elt has a unique clusterId.
      * This occurs when cd-hit runs on multiple files- you will have multiple 'Cluster1's... etc.
      * This hashtable will be used to quickly find clusterIds to load into database
      */
    void generateClusterHashTable();
    void processGffFiles();
    void cleanup();

};

#endif // MULTIGENEREGIONDATAUPLOADER_H
